GH-46421: [C++][Acero] Asofjoin respect PauseProducing from downstream. Version 2 - #51094
Draft
gitmodimo wants to merge 2 commits into
Draft
GH-46421: [C++][Acero] Asofjoin respect PauseProducing from downstream. Version 2#51094gitmodimo wants to merge 2 commits into
gitmodimo wants to merge 2 commits into
Conversation
Contributor
Author
|
@zanmato1984 I do have this PR ready but I first want to establish expanded benchmark baseline. |
Contributor
Author
|
@ursabot please benchmark |
Member
|
Benchmark runs are scheduled for commit 1fa6ddd. Watch https://buildkite.com/apache-arrow and https://conbench.arrow-dev.org for updates. A comment will be posted here when the runs are complete. |
Contributor
Author
|
@ursabot please benchmark |
Member
|
Benchmark runs are scheduled for commit 0d06ddc. Watch https://buildkite.com/apache-arrow and https://conbench.arrow-dev.org for updates. A comment will be posted here when the runs are complete. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rationale for this change
This is a second attempt at solving #46421. The first attempt in #46140 caused a performance regression because output batches merged multiple input batches and could grow very large. This repeatedly grew, reallocated, and copied the unmaterialized table. This PR instead redesigns the node's data flow and addresses the related correctness and execution issues together. The new candidate model also supports an inclusive tolerance range, allowing nearest-neighbor joins across both directions and exact-match exclusion without another special-case option.
What changes are included in this PR?
Execution and materialization model: Replace the dedicated processing thread with a coordinator and independently scheduled processing for each RHS input. The coordinator activates one LHS batch at a time, while each RHS input finds matches and materializes its payload columns independently, implementing the parallel execution proposed in [C++] Parallel asof join node #34135. Once every RHS input completes, the coordinator reuses the original LHS arrays and scalars, resolving the unnecessary copying described in [C++] Asof-joins inefficiently copy the left hand side #41873, appends the RHS columns, and emits one output batch preserving the LHS boundary and index fixing [C++] Make AsofJoinNode robust to input batch scheduling #36651. This also avoids the cross-boundary output growth amplified by the earlier
asof_join_pauseattempt. The same implementation supports threaded, serial, andARROW_ENABLE_THREADING=OFFexecution. Different backpressure semantics replaced old backpressure-controller construction should also eliminate the C++23 incomplete-type build failure reported in [C++][Acero] Build failure on OS X and cppstd 23 #47576.Backpressure: Bound the LHS and each RHS input queue using batch-count watermarks, applying pause and resume upstream as queues cross their watermarks. A downstream pause allows the active LHS batch to finish but prevents another from starting, fixing [C++][Acero] Asofjoin does not propagate pause upstream #46421.
Key matching: Use hashes for lookup followed by exact
by-key comparison, preventing collisions from producing incorrect joins and fixing [C++] AsofJoinNode 128-bit hashing #32894 without switching to 128-bit hashes. Support additional flat key types and scalarbykeys. Benchmarks show no regression. Dictionary-encoded key columns remain unsupported.Non-key fields: Reuse LHS payloads directly and materialize RHS payloads using generic Arrow builders instead of the key encoder. This adds support for nested and dictionary-encoded payloads, including fixed-size lists, and addresses [C++][Acero] Not support type like Fixed Size List for non-key column in asof join node #44729.
on-key normalization: Preserve the ordering of signed integer and temporal keys across zero, fixing [C++][Acero] Negative values in NormalizeTime are mapped to the highest uint64_t values #45876.Null
onkeys: Respect validity bitmaps so null LHS timestamps remain unmatched and null RHS timestamps are never selected, fixing [C++][Acero][Python] Asof join does not detect null times #46780.Tolerance ranges and tie-breaking: Add an
AsofJoinNodeOptionsconstructor taking an inclusive[lower, upper]tolerance range and aprefer_earlier_on_tierule that defaults totrue. A right row is eligible whenright.on - left.onis in the configured range; the closest eligible row is selected, with the preference resolving equal-distance matches on opposite sides. The existing scalar constructor maps to the same backward, forward, or exact interval as before. Ranges such as[-10, -1]exclude exact backward matches, addressing Add option to disable exact matches optional in join_asof #41786 without a separate Boolean option. Entirely non-positive ranges retain one latest candidate perbykey, while ranges extending into the future retain ordered candidates and use binary search.Python bindings: Expose tolerance ranges and
prefer_earlier_on_tiethroughpyarrow.acero.AsofJoinNodeOptions,Table.join_asof, andDataset.join_asof. Python callers may continue passing an integer tolerance or pass a two-element(lower, upper)range.Input ordering: Validate that each input provides ordering compatible with its
onkey and preserve the LHS ordering in the output.Benchmarks: Expand coverage across threading modes, string-key sizes, tolerance directions, and input densities. Local results are approximately 2–109× faster than
mainon my machine.Tests: Re-enable and stabilize the generated-batch backpressure test, fixing [C++] Fix and re-enable Asof Join Backpresure test flakiness #36248. Add regression coverage for jittered input sequencing under threaded and serial execution; input-queue and downstream backpressure; completing or stopping an active LHS batch while paused; preserving LHS boundaries, indices, arrays, scalars, and ordering; exact collision arbitration; additional key and payload types; mixed scalar and array
bykeys; signed and nullonkeys; input-ordering validation; backward and forward exact-match exclusion; nearest-candidate tie-breaking; invalid ranges; normalized-key limits; and the public Python Table and Dataset paths.Are these changes tested?
Yes. All 170 AsofJoin tests pass in both threaded and
ARROW_ENABLE_THREADING=OFFbuilds. The formerly flaky batch-backpressure test also passes multiple repeated runs in each configuration. A freshly built PyArrow passes all 14 Table and Datasetjoin_asoftests, including the new range, tie-breaking, exact-exclusion, and null-time regressions.Are there any user-facing changes?
Additional flat
by-key types are supported, including Boolean, fixed-size binary, and decimal types. Non-key payload columns are no longer restricted to types supported by the key encoder, enabling nested and dictionary-encoded payloads. Dictionary-encodedbykeys remain unsupported.If an LHS
ExecBatchcontains a scalar representing a constant column, AsofJoin preserves it as a scalar in the output instead of materializing it as an array.AsofJoin emits exactly one output batch for each LHS input batch, preserving its length, index, and boundary. This includes zero-length LHS batches.
With threaded execution, matching and materialization for separate RHS inputs may run concurrently. With
use_threads=falseorARROW_ENABLE_THREADING=OFF, the same implementation runs serially without creating a dedicated processing thread.AsofJoin now participates in backpressure. The number of queued batches is bounded per input, and downstream pause prevents new LHS batches from starting after the active batch completes.
Hash collisions can no longer cause different
bykeys to match because hash matches are verified using exact key equality.Signed
onkeys are ordered correctly across zero. Null LHSonvalues remain unmatched, and null RHSonvalues are never selected.The tolerance may be an inclusive lower/upper range. Ranges wholly in the past or future retain the expected directional behavior, while ranges spanning zero select the nearest eligible row. Equal-distance matches prefer the earlier row by default and may be configured to prefer the later row.
PyArrow
Table.join_asofandDataset.join_asofaccept either the existing integer tolerance or a two-element tolerance range, plus the optionalprefer_earlier_on_tieargument.Plans with unordered or incompatible input ordering are now rejected during construction.